#include <iostream>
#include <Active.h>
#include <IGetterMessage.h>
#include <ConcurrentQueue.h>
#include <time.h>
#include <list>
#include <vector>
#include <algorithm>
#include <FlashSort.h>

//#include <time.h>
#define NUM_OF_PARALLEL_THREADS 6
#define NUMBER_OF_ELEMENTS 2000
//using namespace std;

   
class FakeJobMessage: public IGetterMessage<int>
{
  public:
      FakeJobMessage(int l_JobNumber):
      jobNumber(l_JobNumber),
      a(0)
      {//srand (time(NULL));
	
      };
          
private:
      void OnExecuting()
      {
	//std::cout << "Executing job "<< jobNumber << std::endl;
	for (int i=0;i<1000;i++)
	{
	  a+= 1;
	}
	//std::cout << "Job "<< jobNumber << " executed. "<< std::endl;
      }
      
      int getResult()
      {
	return a;
      }
      
private:
  int jobNumber;
  int a;
};

class FakeJobMessageList: public IGetterMessage<boost::shared_ptr<std::list<int> > >
{
  public:
      FakeJobMessageList(int l_JobNumber, boost::shared_ptr<std::list<int> > aList):
      jobNumber(l_JobNumber),
	_listInt( aList )
      {};
      
private:
      void OnExecuting()
      {
	//std::cout << "Executing job "<< jobNumber << std::endl;
	_listInt->sort();
	//std::cout << "Job "<< jobNumber << " executed. "<< std::endl;	
      }
      
      boost::shared_ptr<std::list<int> > getResult()
      {
	return _listInt;
      }
      
private:
  int jobNumber;
  boost::shared_ptr<std::list<int> > _listInt;

};

class FakeJobMessageVector: public IGetterMessage<boost::shared_ptr<std::vector<int> > >
{
  public:
      FakeJobMessageVector(int l_JobNumber, boost::shared_ptr<std::vector<int> > aVector):
      jobNumber(l_JobNumber),
	_vectorInt( aVector )
      {};
      
private:
      void OnExecuting()
      {
	//std::cout << "Executing job "<< jobNumber << std::endl;
	std::sort(_vectorInt->begin(), _vectorInt->end());
	//std::cout << "Job "<< jobNumber << " executed. "<< std::endl;
      }
      
      boost::shared_ptr<std::vector<int> > getResult()
      {
	return _vectorInt;
      }
      
private:
  int jobNumber;
  boost::shared_ptr<std::vector<int> > _vectorInt;

};


void testList(int );
void testVector(int);
void testFlashSort();

int main(int argc, char **argv) {

  std::cout << "Hello, world!" << std::endl;
  //testNumber();
  
  for (int i = 1; i < 16; i++)
  {
    //testList(i);
    //testVector(i);   
  }
   testFlashSort();
  return 0;
}

void testFlashSort()
{
  int size = 10000000;
  
  float *a;
  a=(float*)calloc(size,sizeof(float));
  std::vector<float> bVector;
  struct timespec start, finish;
  double elapsed;
  int called=0;
  srand (time(NULL));
  for (int i=0; i<size;i++)
  {//thoe are perfectly distributed... for more serious test, get it random
    //a[i] = (size-i);//(float)(size+1);
    a[i] = rand()%(size*100);
    bVector.push_back(a[i]);
    //std::cout << a[i] << " " ; 
  }
  std::cout << std::endl ; 

  clock_gettime(CLOCK_MONOTONIC, &start);    
  flashsort(a,size,0.05*size,&called);  
  clock_gettime(CLOCK_MONOTONIC, &finish);    
  
  elapsed = (finish.tv_sec - start.tv_sec);
  elapsed += (finish.tv_nsec - start.tv_nsec) / 1000000000.0;
      
  std::cout << "Result with flashsort is " << elapsed << " s" << std::endl;
  
  clock_gettime(CLOCK_MONOTONIC, &start);
  
  std::sort(bVector.begin(), bVector.end());
  
  clock_gettime(CLOCK_MONOTONIC, &finish);    
  
  elapsed = (finish.tv_sec - start.tv_sec);
  elapsed += (finish.tv_nsec - start.tv_nsec) / 1000000000.0;
      
  std::cout << "Result with std::sort is " << elapsed << " s" << std::endl;
  for (int i=0; i<50;i++)
  {//thoe are perfectly distributed... for more serious test, get it random
    //std::cout << a[i] << " " ; 
  }
  std::cout << std::endl;
}

void testList(int iParallelThreads)
{
  std::cout << "Begin test with Sort" << std::endl;
  if (iParallelThreads==0)
  {
    iParallelThreads=1;
  }
  Active acc(iParallelThreads);
  //Active responses;
  std::list<Active::MessagePtr> JobList;
  
  //sleep(2);
  struct timespec start, finish;
  double elapsed;
  
  for (int i=0; i<20000; i++)
  {  
    boost::shared_ptr<std::list<int> > aList ( new std::list<int> ) ;
    
    for (int aInt=0; aInt <2000 ; aInt ++ )
    {
      aList->push_back((int)aInt%10 );
    }   
    Active::MessagePtr fj (new FakeJobMessageList(i, aList ) );    
    JobList.push_back(fj);
  }
  
  clock_gettime(CLOCK_MONOTONIC, &start);    
  //waiting job.
  std::list<Active::MessagePtr>::iterator iter ;
  for (iter = JobList.begin(); iter != JobList.end(); iter++)
  {
    acc.Send( *iter );    
  }
  
  for (iter = JobList.begin(); iter != JobList.end(); iter++)
  {
    static_cast<FakeJobMessageList *>( iter->get() )->get();    
  }
  
  clock_gettime(CLOCK_MONOTONIC, &finish);
  elapsed = (finish.tv_sec - start.tv_sec);
  elapsed += (finish.tv_nsec - start.tv_nsec) / 1000000000.0;
      
  std::cout << "Result with " << iParallelThreads << " thread/s is: " << elapsed  << std::endl;
  
  
}


void testVector(int iParallelThreads)
{
  std::cout << "Begin test with Sort for Vector" << std::endl;
  if (iParallelThreads==0)
  {
    iParallelThreads=1;
  }
  Active acc(iParallelThreads);
  //Active responses;
  std::list<Active::MessagePtr> JobList;
  
  //sleep(2);
  struct timespec start, finish;
  double elapsed;
  
  for (int i=0; i<20000; i++)
  {  
    boost::shared_ptr<std::vector<int> > aVector ( new std::vector<int> ) ;
    
    aVector->resize(20000);
    for (int aInt=0; aInt <20000 ; aInt ++ )
    {
      (*aVector)[aInt] = aInt%10 ;
    }
    
    Active::MessagePtr fj (new FakeJobMessageVector(i, aVector ) );    
    JobList.push_back(fj);
  }
  
  clock_gettime(CLOCK_MONOTONIC, &start);    
  //waiting job.
  std::list<Active::MessagePtr>::iterator iter ;
  for (iter = JobList.begin(); iter != JobList.end(); iter++)
  {
    acc.Send( *iter );    
  }
  
  for (iter = JobList.begin(); iter != JobList.end(); iter++)
  {
    static_cast<FakeJobMessageVector *>( iter->get() )->get();    
  }
  
  clock_gettime(CLOCK_MONOTONIC, &finish);
  elapsed = (finish.tv_sec - start.tv_sec);
  elapsed += (finish.tv_nsec - start.tv_nsec) / 1000000000.0;
      
  std::cout << "Result with " << iParallelThreads << " thread/s is: " << elapsed  << std::endl; 
}

void testNumber (int iParallelThreads)
{
  if (iParallelThreads==0)
  {
    iParallelThreads=1;
  }
  Active acc(iParallelThreads);
  //Active responses;
  std::list<Active::MessagePtr> JobList;
  
  //sleep(2);
  struct timespec start, finish;
  double elapsed;

  clock_gettime(CLOCK_MONOTONIC, &start);
  
  
  for (int i=0; i<10000; i++){
    Active::MessagePtr fj (new FakeJobMessage(i) );
    acc.Send(fj);
    // std::cout << "Sending Job: " << i << std::endl;
    JobList.push_back(fj);
  }
  
  
    //sleep(5);
  for (int i=0; i<10000; i++){
    Active::MessagePtr fj (new FakeJobMessage(i) );
    acc.Send(fj);
    // std::cout << "Sending Job: " << i << std::endl;
    JobList.push_front(fj);
  }
  //waiting job.
  std::list<Active::MessagePtr>::iterator iter ;
  for (iter = JobList.begin(); iter != JobList.end(); iter++)
  {
    //std::cout << "Result is: " << dynamic_cast<FakeJobMessage *>( iter->get() )->get() << std::endl;
    static_cast<FakeJobMessage *>( iter->get() )->get() ;
    
  }
  clock_gettime(CLOCK_MONOTONIC, &finish);
  elapsed = (finish.tv_sec - start.tv_sec);
  elapsed += (finish.tv_nsec - start.tv_nsec) / 1000000000.0;
      
  std::cout << "Result with " << iParallelThreads << " thread/s is: " << elapsed  << std::endl;
}